import {
  OPENAI_KEY,
  OPENAI_MODEL,
  OPENAI_MAX_TOKEN,
  ADJACENT_MESSAGE_MAX_INTERVAL,
  NO_MESSAGE,
  CLEAR_MESSAGE,
  HELP_MESSAGE,
  baseMessage,
  GET_MORE_MESSAGE,
  getMessageList,
} from "./config.js";
import { Request } from "js-request-lib";
import { JSLog } from "js-log-lib";
import { Message, Event } from "./db.js";
import { catchAwait, jsonToString } from "utils-lib-js-lite";
import { IAnswer } from "./answer.js";
const { log } = new JSLog({});

export const openAiRequest = new Request("https://openkey.cloud");
export const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
export function toXML(payload, content) {
  const timestamp = Date.now();
  const { ToUserName: fromUserName, FromUserName: toUserName } = payload;
  return `
    <xml>
      <ToUserName><![CDATA[${toUserName}]]></ToUserName>
      <FromUserName><![CDATA[${fromUserName}]]></FromUserName>
      <CreateTime>${timestamp}</CreateTime>
      <MsgType><![CDATA[text]]></MsgType>
      <Content><![CDATA[${content}]]></Content>
    </xml>
    `;
}

export async function processCommandText(
  { sessionId, question },
  msg: Partial<IAnswer>
) {
  const messageList = baseMessage;
  // 清理历史会话
  if (question === messageList[1].name) {
    await Message.put(sessionId, {});
    return CLEAR_MESSAGE;
  } else if (msg.value === "more") {
    // 更多
    return GET_MORE_MESSAGE();
  } else if (msg.name === question) {
    // 索引对应的指令
    return msg.return ?? HELP_MESSAGE;
  } else {
    return HELP_MESSAGE;
  }
}

// 构建 prompt
export async function buildOpenAIPrompt(sessionId, question) {
  let prompt = [];

  // 获取最近的历史会话
  const now = new Date();
  // const earliestAt = new Date(now.getTime() - CONVERSATION_MAX_AGE)
  const historyMessages = (await Message.get(sessionId)) ?? { list: [] };
  // .sort({ createdAt: -1 })
  // .limit(LIMIT_HISTORY_MESSAGES)
  // .find();
  let lastMessageTime = now;
  let tokenSize = 0;
  for (const message of historyMessages.list) {
    // 如果历史会话记录大于 OPENAI_MAX_TOKEN 或 两次会话间隔超过 10 分钟，则停止添加历史会话
    const timeSinceLastMessage = lastMessageTime
      ? Number(lastMessageTime) - message.createdAt
      : 0;
    if (
      tokenSize > OPENAI_MAX_TOKEN ||
      timeSinceLastMessage > ADJACENT_MESSAGE_MAX_INTERVAL
    ) {
      break;
    }
    prompt.unshift({ role: "assistant", content: message.answer ?? "" });
    prompt.unshift({ role: "user", content: message.question ?? "" });
    tokenSize += message.token;
    lastMessageTime = message.createdAt;
  }
  prompt.push({ role: "user", content: question });
  return prompt;
}

// 获取 OpenAI API 的回复
async function getOpenAIReply(prompt) {
  const data = {
    model: OPENAI_MODEL,
    messages: prompt,
  };
  const [error, response]: [Error, Partial<any>] = await catchAwait(
    openAiRequest.POST("/v1/chat/completions", {}, data as any, {
      headers: {
        Authorization: `Bearer ${OPENAI_KEY}`,
        "Content-Type": "application/json",
      },
    })
  );
  if (error) {
    console.error(error);
    return {
      error: "问题太难了 出错了. (uДu〃).",
    };
  }
  if (response.status === 429) {
    return {
      error: "问题太多了，我有点眩晕，请稍后再试",
    };
  }
  // 去除多余的换行
  return {
    answer:
      response?.choices[0].message.content.replace("\n\n", "") ??
      ("" as string),
  };
}

// 处理文本回复消息
export async function replyText(message) {
  const { question, sessionId, msgid } = message;
  // 检查是否是重试操作
  const messageList = baseMessage;
  if (question === messageList[0].name) {
    const now = new Date();
    // const earliestAt = new Date(now.getTime() - CONVERSATION_MAX_AGE)
    const lastMessage = await Message.get(sessionId);
    // .sort({ createdAt: -1 })
    // .findOne();

    if (lastMessage?.list?.length) {
      const { answer, question } = lastMessage?.list[0];
      return `${question}\n------------\n${answer}`;
    }

    return NO_MESSAGE;
  }
  // 发送指令
  const msg = messageList.find((it) => it.name === question);
  if (msg) {
    return await processCommandText(message, msg);
  }
  // 获取更多指令
  const moreList = await getMessageList();
  const more = moreList.find((it) => it.name === question);
  if (more) {
    return more.return;
  }
  // OpenAI 回复内容
  const prompt = await buildOpenAIPrompt(sessionId, question);
  console.log(prompt);

  const { error, answer } = await getOpenAIReply(prompt);
  console.debug(
    `[OpenAI reply] sessionId: ${sessionId}; prompt: ${prompt}; question: ${question}; answer: ${answer}`
  );
  if (error) {
    console.error(
      `sessionId: ${sessionId}; question: ${question}; error: ${error}`
    );
    return error;
  }
  // 保存消息
  const token = question.length + answer.length;
  const history = (await Message.get(sessionId)) ?? { list: [] };
  history.list.unshift({ token, question, answer, ...message });
  await Message.put(sessionId, history);
  console.debug("save msg", history);
  return answer;
}

// 验证是否是重复推送事件
export async function checkEvent(payload) {
  const eventId = payload?.MsgId;

  const event = (await Event.get(eventId)) ?? null;
  if (event) return true;
  await Event.put(eventId, { payload });
  return false;
}

export const logger = (req, _, next) => {
  const { url, method, params, body, query, headers } = req;
  log({ text: `[${method} ${url}]`, color: "green" })
    .log({ text: `${jsonToString(params)}`, color: "blue" })
    .log({ text: `${jsonToString(body)}`, color: "red" })
    .log({ text: `${jsonToString(query)}`, color: "yellow" })
    .log({ text: `${jsonToString(headers)}`, color: "cyan" });
  next();
};
